/*-------------------------------------------------------------------------
 *
 * aggregatecmds.c
 *
 *	  聚合操作命令的例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, 加利福尼亚大学董事会
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/aggregatecmds.c
 *
 * DESCRIPTION
 *	  “DefineFoo”例程获取解析树并挑选出
 *	  适当的参数/标志，将结果传递给对应的
 *	  “FooDefine”例程（在src/catalog），进行实际的目录处理。
 *	  这些例程还验证用户执行命令的权限。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/dependency.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"


static char fc_extractModify(DefElem *fc_defel);


/*
 *	定义聚合
 *
 * "oldstyle" 表示旧的（8.2版本之前）样式，其中聚合输入类型
 * 通过参数中的 BASETYPE 元素指定。否则，
 * "args" 是一对，其第一个元素是定义 agg 的参数（直接和聚合的） 
 * 的 FunctionParameter 结构的列表，第二个元素是一个 Integer 节点，表示直接参数的数量， 
 * 或 -1 如果这不是一个有序集合聚合。
 * "parameters" 是一个 DefElem 列表，表示 agg 的定义子句。
 */
ObjectAddress DefineAggregate(ParseState *fc_pstate,
				List *fc_name,
				List *fc_args,
				bool fc_oldstyle,
				List *fc_parameters,
				bool fc_replace)
{
	char	   *fc_aggName;
	Oid			fc_aggNamespace;
	AclResult	fc_aclresult;
	char		fc_aggKind = AGGKIND_NORMAL;
	List	   *fc_transfuncName = NIL;
	List	   *fc_finalfuncName = NIL;
	List	   *fc_combinefuncName = NIL;
	List	   *fc_serialfuncName = NIL;
	List	   *fc_deserialfuncName = NIL;
	List	   *fc_mtransfuncName = NIL;
	List	   *fc_minvtransfuncName = NIL;
	List	   *fc_mfinalfuncName = NIL;
	bool		fc_finalfuncExtraArgs = false;
	bool		fc_mfinalfuncExtraArgs = false;
	char		fc_finalfuncModify = 0;
	char		fc_mfinalfuncModify = 0;
	List	   *fc_sortoperatorName = NIL;
	TypeName   *fc_baseType = NULL;
	TypeName   *fc_transType = NULL;
	TypeName   *fc_mtransType = NULL;
	int32		fc_transSpace = 0;
	int32		fc_mtransSpace = 0;
	char	   *fc_initval = NULL;
	char	   *fc_minitval = NULL;
	char	   *fc_parallel = NULL;
	int			fc_numArgs;
	int			fc_numDirectArgs = 0;
	oidvector  *fc_parameterTypes;
	ArrayType  *fc_allParameterTypes;
	ArrayType  *fc_parameterModes;
	ArrayType  *fc_parameterNames;
	List	   *fc_parameterDefaults;
	Oid			fc_variadicArgType;
	Oid			fc_transTypeId;
	Oid			fc_mtransTypeId = InvalidOid;
	char		fc_transTypeType;
	char		fc_mtransTypeType = 0;
	char		fc_proparallel = PROPARALLEL_UNSAFE;
	ListCell   *fc_pl;

	/* 将名称列表转换为一个名称和命名空间 */
	fc_aggNamespace = QualifiedNameGetCreationNamespace(fc_name, &fc_aggName);

	/* 检查我们在目标命名空间中是否有创建权限 */
	fc_aclresult = pg_namespace_aclcheck(fc_aggNamespace, GetUserId(), ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_aggNamespace));

	/* 解构聚合参数语法产出的输出 */
	if (!fc_oldstyle)
	{
		Assert(list_length(fc_args) == 2);
		fc_numDirectArgs = intVal(lsecond(fc_args));
		if (fc_numDirectArgs >= 0)
			fc_aggKind = AGGKIND_ORDERED_SET;
		else
			fc_numDirectArgs = 0;
		fc_args = linitial_node(List, fc_args);
	}

	/* 检查聚合的定义子句 */
	foreach(fc_pl, fc_parameters)
	{
		DefElem    *fc_defel = lfirst_node(DefElem, fc_pl);

		/*
		 * sfunc1、stype1 和 initcond1 被接受为 sfunc、stype、initcond 的过时拼写。
		 */
		if (strcmp(fc_defel->defname, "sfunc") == 0)
			fc_transfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "sfunc1") == 0)
			fc_transfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "finalfunc") == 0)
			fc_finalfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "combinefunc") == 0)
			fc_combinefuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "serialfunc") == 0)
			fc_serialfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "deserialfunc") == 0)
			fc_deserialfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "msfunc") == 0)
			fc_mtransfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "minvfunc") == 0)
			fc_minvtransfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "mfinalfunc") == 0)
			fc_mfinalfuncName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "finalfunc_extra") == 0)
			fc_finalfuncExtraArgs = defGetBoolean(fc_defel);
		else if (strcmp(fc_defel->defname, "mfinalfunc_extra") == 0)
			fc_mfinalfuncExtraArgs = defGetBoolean(fc_defel);
		else if (strcmp(fc_defel->defname, "finalfunc_modify") == 0)
			fc_finalfuncModify = fc_extractModify(fc_defel);
		else if (strcmp(fc_defel->defname, "mfinalfunc_modify") == 0)
			fc_mfinalfuncModify = fc_extractModify(fc_defel);
		else if (strcmp(fc_defel->defname, "sortop") == 0)
			fc_sortoperatorName = defGetQualifiedName(fc_defel);
		else if (strcmp(fc_defel->defname, "basetype") == 0)
			fc_baseType = defGetTypeName(fc_defel);
		else if (strcmp(fc_defel->defname, "hypothetical") == 0)
		{
			if (defGetBoolean(fc_defel))
			{
				if (fc_aggKind == AGGKIND_NORMAL)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
							 errmsg("only ordered-set aggregates can be hypothetical")));
				fc_aggKind = AGGKIND_HYPOTHETICAL;
			}
		}
		else if (strcmp(fc_defel->defname, "stype") == 0)
			fc_transType = defGetTypeName(fc_defel);
		else if (strcmp(fc_defel->defname, "stype1") == 0)
			fc_transType = defGetTypeName(fc_defel);
		else if (strcmp(fc_defel->defname, "sspace") == 0)
			fc_transSpace = defGetInt32(fc_defel);
		else if (strcmp(fc_defel->defname, "mstype") == 0)
			fc_mtransType = defGetTypeName(fc_defel);
		else if (strcmp(fc_defel->defname, "msspace") == 0)
			fc_mtransSpace = defGetInt32(fc_defel);
		else if (strcmp(fc_defel->defname, "initcond") == 0)
			fc_initval = defGetString(fc_defel);
		else if (strcmp(fc_defel->defname, "initcond1") == 0)
			fc_initval = defGetString(fc_defel);
		else if (strcmp(fc_defel->defname, "minitcond") == 0)
			fc_minitval = defGetString(fc_defel);
		else if (strcmp(fc_defel->defname, "parallel") == 0)
			fc_parallel = defGetString(fc_defel);
		else
			ereport(WARNING,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("aggregate attribute \"%s\" not recognized",
							fc_defel->defname)));
	}

	/*
	 * 确保我们有所需的定义
	 */
	if (fc_transType == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("aggregate stype must be specified")));
	if (fc_transfuncName == NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("aggregate sfunc must be specified")));

	/*
	 * 如果提供了 mtransType，则 mtransfuncName 和 minvtransfuncName 也必须提供；
	 * 否则，不应该提供任何移动聚合选项。
	 */
	if (fc_mtransType != NULL)
	{
		if (fc_mtransfuncName == NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate msfunc must be specified when mstype is specified")));
		if (fc_minvtransfuncName == NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate minvfunc must be specified when mstype is specified")));
	}
	else
	{
		if (fc_mtransfuncName != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate msfunc must not be specified without mstype")));
		if (fc_minvtransfuncName != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate minvfunc must not be specified without mstype")));
		if (fc_mfinalfuncName != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate mfinalfunc must not be specified without mstype")));
		if (fc_mtransSpace != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate msspace must not be specified without mstype")));
		if (fc_minitval != NULL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate minitcond must not be specified without mstype")));
	}

	/*
	 * 修改标志的默认值只能在知道 aggKind 后才能确定。
	 */
	if (fc_finalfuncModify == 0)
		fc_finalfuncModify = (fc_aggKind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
	if (fc_mfinalfuncModify == 0)
		fc_mfinalfuncModify = (fc_aggKind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;

	/*
	 * 查找聚合的输入数据类型。
	 */
	if (fc_oldstyle)
	{
		/*
		 * 旧样式：使用 basetype 参数。这支持零或一个输入的聚合，
		 * 输入类型 ANY 意味着零输入。
		 *
		 * 从历史上看，我们允许命令看起来像 basetype = 'ANY'，
		 * 以便我们必须对名称 ANY 进行不区分大小写的比较。哎。
		 */
		Oid			fc_aggArgTypes[1];

		if (fc_baseType == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate input type must be specified")));

		if (pg_strcasecmp(TypeNameToString(fc_baseType), "ANY") == 0)
		{
			fc_numArgs = 0;
			fc_aggArgTypes[0] = InvalidOid;
		}
		else
		{
			fc_numArgs = 1;
			fc_aggArgTypes[0] = typenameTypeId(NULL, fc_baseType);
		}
		fc_parameterTypes = buildoidvector(fc_aggArgTypes, fc_numArgs);
		fc_allParameterTypes = NULL;
		fc_parameterModes = NULL;
		fc_parameterNames = NULL;
		fc_parameterDefaults = NIL;
		fc_variadicArgType = InvalidOid;
	}
	else
	{
		/*
		 * 新样式：args 是 FunctionParameters 的一个列表（可能包含零个）。
		 * 我们共享 functioncmds.c 中处理它们的代码。
		 */
		Oid			fc_requiredResultType;

		if (fc_baseType != NULL)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("basetype is redundant with aggregate input type specification")));

		fc_numArgs = list_length(fc_args);
		interpret_function_parameter_list(fc_pstate,
										  fc_args,
										  InvalidOid,
										  OBJECT_AGGREGATE,
										  &fc_parameterTypes,
										  NULL,
										  &fc_allParameterTypes,
										  &fc_parameterModes,
										  &fc_parameterNames,
										  NULL,
										  &fc_parameterDefaults,
										  &fc_variadicArgType,
										  &fc_requiredResultType);
		/* 当前语法不允许参数默认值 */
		Assert(fc_parameterDefaults == NIL);
		/* 也不应该有任何 OUT 参数 */
		Assert(fc_requiredResultType == InvalidOid);
	}

	/*
	 * 查找聚合的 transtype。
	 *
	 * transtype 不能是伪类型，因为我们需要能够存储
	 * transtype 的值。然而，在某些情况下，我们可以允许多态 transtype 
	 * （AggregateCreate 将检查）。此外，我们允许 "internal"
	 * 用于希望传递指向私有数据结构的指针的函数；
	 * 但仅允许超级用户使用，因为连接不兼容的使用内部函数到聚合中可能会崩溃系统（或更糟）。
	 */
	fc_transTypeId = typenameTypeId(NULL, fc_transType);
	fc_transTypeType = get_typtype(fc_transTypeId);
	if (fc_transTypeType == TYPTYPE_PSEUDO &&
		!IsPolymorphicType(fc_transTypeId))
	{
		if (fc_transTypeId == INTERNALOID && superuser())
			 /* 可以 */ ;
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("aggregate transition data type cannot be %s",
							format_type_be(fc_transTypeId))));
	}

	if (fc_serialfuncName && fc_deserialfuncName)
	{
		/*
		 * 仅需/允许对 transtype INTERNAL 进行序列化。
		 */
		if (fc_transTypeId != INTERNALOID)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("serialization functions may be specified only when the aggregate transition data type is %s",
							format_type_be(INTERNALOID))));
	}
	else if (fc_serialfuncName || fc_deserialfuncName)
	{
		/*
		 * 不能仅指定一个函数而不指定另一个。
		 */
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("must specify both or neither of serialization and deserialization functions")));
	}

	/*
	 * 如果指定了移动聚合 transtype，则查找该类。和 transtype 一样的
	 * 限制。
	 */
	if (fc_mtransType)
	{
		fc_mtransTypeId = typenameTypeId(NULL, fc_mtransType);
		fc_mtransTypeType = get_typtype(fc_mtransTypeId);
		if (fc_mtransTypeType == TYPTYPE_PSEUDO &&
			!IsPolymorphicType(fc_mtransTypeId))
		{
			if (fc_mtransTypeId == INTERNALOID && superuser())
				 /* 可以 */ ;
			else
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("aggregate transition data type cannot be %s",
								format_type_be(fc_mtransTypeId))));
		}
	}

	/*
	 * 如果我们有一个初始化值，并且它不是伪类型（特别是多态类型），请确保它可以被类型的输入函数接受。
	 * 我们将把初始化值存储为文本，因为输入函数不一定是不可变的（考虑时间戳中的“现在”），
	 * 我们希望使用运行时而不是创建时对值的解释。
	 * 然而，如果它是一个不正确的值，在 CREATE AGGREGATE 时投诉似乎要友好得多。
	 */
	if (fc_initval && fc_transTypeType != TYPTYPE_PSEUDO)
	{
		Oid			fc_typinput,
					fc_typioparam;

		getTypeInputInfo(fc_transTypeId, &fc_typinput, &fc_typioparam);
		(void) OidInputFunctionCall(fc_typinput, fc_initval, fc_typioparam, -1);
	}

	/*
	 * 移动聚合的初始化值也是如此。
	 */
	if (fc_minitval && fc_mtransTypeType != TYPTYPE_PSEUDO)
	{
		Oid			fc_typinput,
					fc_typioparam;

		getTypeInputInfo(fc_mtransTypeId, &fc_typinput, &fc_typioparam);
		(void) OidInputFunctionCall(fc_typinput, fc_minitval, fc_typioparam, -1);
	}

	if (fc_parallel)
	{
		if (strcmp(fc_parallel, "safe") == 0)
			fc_proparallel = PROPARALLEL_SAFE;
		else if (strcmp(fc_parallel, "restricted") == 0)
			fc_proparallel = PROPARALLEL_RESTRICTED;
		else if (strcmp(fc_parallel, "unsafe") == 0)
			fc_proparallel = PROPARALLEL_UNSAFE;
		else
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE")));
	}

	/*
	 * 大部分参数检查是在 AggregateCreate 内部完成的
	 */
	return AggregateCreate(fc_aggName, /* 聚合名称 */
						   fc_aggNamespace,	/* 名称空间 */
						   fc_replace,
						   fc_aggKind,
						   fc_numArgs,
						   fc_numDirectArgs,
						   fc_parameterTypes,
						   PointerGetDatum(fc_allParameterTypes),
						   PointerGetDatum(fc_parameterModes),
						   PointerGetDatum(fc_parameterNames),
						   fc_parameterDefaults,
						   fc_variadicArgType,
						   fc_transfuncName,	/* 步骤函数名称 */
						   fc_finalfuncName,	/* 最终函数名称 */
						   fc_combinefuncName, /* 合并函数名称 */
						   fc_serialfuncName,	/* 序列化函数名称 */
						   fc_deserialfuncName,	/* 反序列化函数名称 */
						   fc_mtransfuncName,	/* 正向转换函数名称 */
						   fc_minvtransfuncName,	/* 逆向转换函数名称 */
						   fc_mfinalfuncName,	/* 最终函数名称 */
						   fc_finalfuncExtraArgs,
						   fc_mfinalfuncExtraArgs,
						   fc_finalfuncModify,
						   fc_mfinalfuncModify,
						   fc_sortoperatorName,	/* 排序运算符名称 */
						   fc_transTypeId, /* 转换数据类型 */
						   fc_transSpace,	/* 转换空间 */
						   fc_mtransTypeId,	/* 转换数据类型 */
						   fc_mtransSpace, /* 转换空间 */
						   fc_initval, /* 初始条件 */
						   fc_minitval,	/* 初始条件 */
						   fc_proparallel);	/* 是否并行安全？ */
}

/*
 * 将 [m]finalfunc_modify 的字符串形式转换为目录表示
 */
static char fc_extractModify(DefElem *fc_defel)
{
	char	   *fc_val = defGetString(fc_defel);

	if (strcmp(fc_val, "read_only") == 0)
		return AGGMODIFY_READ_ONLY;
	if (strcmp(fc_val, "shareable") == 0)
		return AGGMODIFY_SHAREABLE;
	if (strcmp(fc_val, "read_write") == 0)
		return AGGMODIFY_READ_WRITE;
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("parameter \"%s\" must be READ_ONLY, SHAREABLE, or READ_WRITE",
					fc_defel->defname)));
	return 0;					/* 保持编译器安静 */
}
